package dbms;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;

public class Table {

	private String name;
	private HashMap<String, Column<?>> columns;

	// CONSTRUCTOR

	public Table(String name) {
		this.name = name;
		columns = new HashMap<String, Column<?>>();
	}

	// METADATA

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	// DATA

	public Set<Entry<String, Column<?>>> getColumnMap() {
		return columns.entrySet();
	}

	public Collection<Column<?>> getColumns() {
		return columns.values();
	}

	public int getWidth() {
		return columns.size();
	}

	public int getHeight() {
		return columns.values().iterator().next().getHeight();
	}

	// TABLE

	public Table getSubTable(String[] clmn) {
		if (clmn != null) {
			Table out = new Table(getName());
			for (Column<?> tableColumn : columns.values())
				for (String inCols : clmn)
					if (tableColumn.getName().equals(inCols)) {
						try {
							out.addColumn(tableColumn.shallowCopy());
						} catch (DBException e) {}
						break;
					}
			return out;
		} else {
			return this;
		}
	}

	public String[] getColumnNames() {
		Set<String> cols = columns.keySet();
		String[] out = new String[cols.size()];
		int i = -1;
		for (String col : cols)
			out[++i] = col;
		return out;
	}

	public String[][] getTable() {
		String[] cols = getColumnNames();
		String[][] out = new String[getHeight()][getWidth()];
		for (int x = 0; x < getWidth(); x++) {
			String col = cols[x];
			for (int y = 0; y < getHeight(); y++) {
				try {
					out[y][x] = getColumn(col).getCell(y).getValue() + "";
				} catch (DBException e) {}
			}
		}
		return out;
	}

	public String[][] getTableWithHeaders() {
		String[][] out = new String[getHeight() + 1][getWidth()];
		// headers
		String[] cols = getColumnNames();
		int i = -1;
		for (String col : cols)
			out[0][++i] = col;
		// cells
		for (int x = 0; x < getWidth(); x++) {
			String col = cols[x];
			for (int y = 0; y < getHeight(); y++) {
				try {
					out[y + 1][x] = getColumn(col).getCell(y).getValue() + "";
				} catch (DBException e) {}
			}
		}
		return out;
	}

	// SELECT

	public Column<?> getColumn(String clm) throws DBException {
		Column<?> out = columns.get(clm);
		if (out == null)
			throw new DBException(DBException.CLM_NOTFOUND);
		return columns.get(clm);
	}

	public HashMap<String, Object> getRow(int y) throws DBException {
		HashMap<String, Object> out = new HashMap<String, Object>();
		Collection<Column<?>> clms = getColumns();
		for (Column<?> clm : clms)
			out.put(clm.getName(), clm.getCell(y).getValue());
		return out;
	}

	// INSERT

	// column

	public void addColumn(Column<?> c) throws DBException {
		if (columns.containsKey(c.getName()))
			throw new DBException(DBException.CLM_DUPLICATE);
		columns.put(c.getName(), c);
	}

	public void addColumn(String colName, String colType) throws DBException {
		Column c;
		int i = colType.indexOf('('), j = colType.indexOf(')');
		final int type = Type.getTypeID(colType.substring(0, (i < 0) ? colType.length() : i));
		// VARCHAR
		if (type == Type.STRING) {
			int length = 0;
			if (colType.contains("("))
				length = Integer.parseInt(colType.substring(i + 1, j));
			else
				length = Integer.MAX_VALUE;
			c = new Column<String>(colName, length) {

				@Override
				public int type() {
					return type;
				}
			};
		}
		// ELSE
		else
			c = new Column(colName) {

				@Override
				public int type() {
					return type;
				}
			};
		addColumn(c);
	}

	// row

	public void addRow(String[] in) throws DBException {
		Object[] values = new Object[in.length];
		int i = 0;
		for (Column c : columns.values()) {
			values[i] = Type.parse(in[i], c.type());
			++i;
		}
		addRow(values);
	}

	public void addRow(Object[] in) throws DBException {
		String[] cols = getColumnNames();
		for (int i = 0; i < cols.length; i++) {
			String c = cols[i];
			if (in[i] == null)
				columns.get(c).addCell(null);
			else {
				columns.get(c).addCell(in[i]);
			}
		}
		for (String c : getColumnNames()) {}
	}

	public void addRow(HashMap<String, Object> in) throws DBException {
		int i = 0;
		Set<String> cols = columns.keySet();
		for (String col : cols) {
			Class type = Type.getTypeClass(columns.get(col).type());
			if (in == null)
				columns.get(col).addCell(type.cast(null));
			else
				columns.get(col).addCell(type.cast(in.get(col)));
			++i;
		}
	}

	// UPDATE

	public void setCell(int x, int y, String s) throws DBException {
		setCell(getColumnNames()[x], y, s);
	}

	public void setCell(String column, int y, String s) throws DBException {
		Column<?> col = columns.get(column);
		col.setCell(y, Type.parse(s, col.type()));
	}

	// DELETE

	// column

	public void removeColumn(Column<?> column) {
		columns.remove(column.getName());
	}

	public void removeColumn(String columnName) {
		columns.remove(columnName);
	}

	// row

	public void removeRow(int y) throws DBException {
		for (Column c : columns.values())
			c.removeCell(y);
	}

	// STRINGS

	@Override
	public String toString() {
		String out = "";
		for (String header : getColumnNames())
			out += header + "\t";
		for (int y = 0; y < getHeight(); y++) {
			out += "\n";
			for (Column col : columns.values()) {
				try {
					out += col.getCell(y).getValue() + "\t";
				} catch (DBException e) {}
			}
		}
		return out;
	}
}